﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using DotNet.DevOps.Tasks.Util;

namespace DotNet.DevOps.Tasks.AssemblyInfo.Updaters
{
    internal abstract class PropertyUpdater
    {
        protected readonly AssemblyInfo asm;
        private Regex entryRegex;
        private Regex valueRegex = new Regex(@"\""(?<value>.*)\""", RegexOptions.Compiled | RegexOptions.Singleline);
        protected ILog Log;

        protected PropertyUpdater(AssemblyInfo asm, ILog logger)
        {
            this.asm = asm;
            Log = logger;
        }

        protected abstract Dictionary<String, String> GetPropertyNameValuePairs();

        protected abstract String GetNewEntryValue(String keyName, String oldValue);

        public string UpdateProperties(String contentToUpdate)
        {
            var entries = GetPropertyNameValuePairs();

            contentToUpdate = entries.Aggregate(contentToUpdate, UpdateOrAddEntry);

            return contentToUpdate;
        }

        private String UpdateOrAddEntry(String fileContent, KeyValuePair<String, String> entry)
        {
            var pattern = String.Format("{0}{1}{2}", @"^\s*\[\s*assembly\s*:\s*", entry.Key, @"\(\s*"".*""\s*\)\s*\]\s*$");

            entryRegex = new Regex(pattern, RegexOptions.Multiline);

            if (entryRegex.IsMatch(fileContent) == false)
            {
                return AddEntry(fileContent, entry.Key);
            }
            return UpdateEntry(fileContent, entry.Key);
        }

        private String UpdateEntry(String fileContent, String keyName)
        {
            var oldValue = GetEntryValue(fileContent);
            var newValue = GetNewEntryValue(keyName, oldValue);

            if (newValue != oldValue)
            {
                var newEntry = CreateNewEntry(keyName, newValue);

                Log.LogInfo(String.Format("Updating {0}: {1} -> {2}", keyName, oldValue, GetEntryValue(newEntry)));

                fileContent = entryRegex.Replace(fileContent, newEntry);
            }

            return fileContent;
        }

        private String GetEntryValue(String fileContent)
        {
            var entry = entryRegex.Match(fileContent).Value;
            return valueRegex.Match(entry).Groups["value"].Value;
        }

        private String AddEntry(String fileContent, String keyName)
        {
            var newEntry = CreateNewEntry(keyName, GetNewEntryValue(keyName, null));

            if(IsEntryValid(newEntry) == true)
            {
                return AppendEntryToFile(fileContent, keyName ,newEntry);
            }

            return fileContent;
        }

        private static bool IsEntryValid(string newEntry)
        {
            return newEntry.Contains("(\"\")") == false;
        }

        private string AppendEntryToFile(string fileContent, string keyName, string newEntry)
        {
            if (asm.AddIfMissing == true)
            {
                Log.LogInfo(String.Format("Adding {0}: {1}", keyName, newEntry));
                return String.Format("{0}\r\n{1}", fileContent, String.Format("[assembly: {0}", newEntry));
            }
            Log.LogWarning(String.Format("{0} was not added since AddIfMissing is set to FALSE", newEntry));

            return fileContent;
        }

        private static String CreateNewEntry(String key, String value)
        {
            return String.Format("[assembly: {0}(\"{1}\")]", key, value);
        }
    }
}
